/**
 * 精简版CryptoJS，专用于S3签名
 * 仅包含SHA256和HMAC-SHA256算法
 */

// 基础工具函数
const CryptoJSMini = (function () {
  // 转换为大端字节序的32位字
  function bytesToWords(bytes) {
    const words = [];
    for (let i = 0, b = 0; i < bytes.length; i++, b += 8) {
      words[b >>> 5] |= bytes[i] << (24 - b % 32);
    }
    return words;
  }

  // 转换字字节
  function wordsToBytes(words) {
    const bytes = [];
    for (let b = 0; b < words.length * 32; b += 8) {
      bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF);
    }
    return bytes;
  }

  // 将字符串转换为字节数组
  function stringToBytes(str) {
    const bytes = [];
    for (let i = 0; i < str.length; i++) {
      bytes.push(str.charCodeAt(i) & 0xFF);
    }
    return bytes;
  }

  // 将十六进制字符串转换为字节数组
  function hexToBytes(hex) {
    const bytes = [];
    for (let c = 0; c < hex.length; c += 2) {
      bytes.push(parseInt(hex.substr(c, 2), 16));
    }
    return bytes;
  }

  // 将字节数组转换为十六进制字符串
  function bytesToHex(bytes) {
    const hex = [];
    for (let i = 0; i < bytes.length; i++) {
      hex.push((bytes[i] >>> 4).toString(16));
      hex.push((bytes[i] & 0xF).toString(16));
    }
    return hex.join('');
  }

  // 将字转换为十六进制字符串
  function wordsToHex(words) {
    return bytesToHex(wordsToBytes(words));
  }

  // 将字符串转换为字
  function stringToWords(str) {
    return bytesToWords(stringToBytes(str));
  }

  // 位移循环
  function rotl(n, b) {
    return (n << b) | (n >>> (32 - b));
  }

  // 将字转换为字符串
  function wordsToString(words) {
    return String.fromCharCode.apply(String, wordsToBytes(words));
  }

  // SHA256的K常量
  const K = [
    0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5,
    0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5,
    0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3,
    0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174,
    0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC,
    0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA,
    0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7,
    0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967,
    0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13,
    0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85,
    0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3,
    0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070,
    0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5,
    0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3,
    0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208,
    0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2
  ];

  // SHA256算法实现
  function SHA256(message) {
    // 转换字符串为字节数组
    let m;
    if (typeof message === 'string') {
      m = stringToBytes(message);
    } else {
      m = message;
    }

    // 初始哈希值
    const H = [
      0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
      0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19
    ];

    // 消息长度（单位：位）
    const ml = m.length * 8;

    // 填充
    m.push(0x80);
    
    // 填充至满足 (length + 1 + 8) % 64 = 0
    while (m.length % 64 !== 56) {
      m.push(0);
    }

    // 添加消息长度（64位整数）
    for (let i = 0; i < 8; i++) {
      m.push((ml >>> ((7 - i) * 8)) & 0xFF);
    }

    // 将字节数组转换为字数组
    const w = bytesToWords(m);

    // 主循环
    for (let i = 0; i < w.length; i += 16) {
      const a = H[0];
      const b = H[1];
      const c = H[2];
      const d = H[3];
      const e = H[4];
      const f = H[5];
      const g = H[6];
      const h = H[7];

      // 主循环内的64轮
      for (let j = 0; j < 64; j++) {
        let W;
        if (j < 16) {
          W = w[i + j];
        } else {
          const gamma0x = w[i + ((j - 15) % 16)];
          const gamma0 = ((gamma0x >>> 7) | (gamma0x << 25)) ^ 
                        ((gamma0x >>> 18) | (gamma0x << 14)) ^ 
                        (gamma0x >>> 3);

          const gamma1x = w[i + ((j - 2) % 16)];
          const gamma1 = ((gamma1x >>> 17) | (gamma1x << 15)) ^ 
                        ((gamma1x >>> 19) | (gamma1x << 13)) ^ 
                        (gamma1x >>> 10);

          W = w[i + ((j - 16) % 16)] + gamma0 + w[i + ((j - 7) % 16)] + gamma1;
        }

        // 更新W数组
        w[i + (j % 16)] = W >>> 0;

        // 压缩函数
        const ch = (e & f) ^ (~e & g);
        const maj = (a & b) ^ (a & c) ^ (b & c);
        
        const sigma0 = ((a >>> 2) | (a << 30)) ^ 
                       ((a >>> 13) | (a << 19)) ^ 
                       ((a >>> 22) | (a << 10));
        
        const sigma1 = ((e >>> 6) | (e << 26)) ^ 
                       ((e >>> 11) | (e << 21)) ^ 
                       ((e >>> 25) | (e << 7));

        const t1 = h + sigma1 + ch + K[j] + W;
        const t2 = sigma0 + maj;

        h = g;
        g = f;
        f = e;
        e = (d + t1) >>> 0;
        d = c;
        c = b;
        b = a;
        a = (t1 + t2) >>> 0;
      }

      // 更新哈希值
      H[0] = (H[0] + a) >>> 0;
      H[1] = (H[1] + b) >>> 0;
      H[2] = (H[2] + c) >>> 0;
      H[3] = (H[3] + d) >>> 0;
      H[4] = (H[4] + e) >>> 0;
      H[5] = (H[5] + f) >>> 0;
      H[6] = (H[6] + g) >>> 0;
      H[7] = (H[7] + h) >>> 0;
    }

    return H;
  }

  // HMAC-SHA256算法实现
  function HMAC_SHA256(key, message) {
    // 将键转换为字节数组
    let k;
    if (typeof key === 'string') {
      k = stringToBytes(key);
    } else {
      k = key;
    }

    // 对密钥进行处理
    if (k.length > 64) {
      k = wordsToBytes(SHA256(k));
    }

    // 填充密钥到64字节
    const innerPadding = Array(64);
    const outerPadding = Array(64);
    for (let i = 0; i < 64; i++) {
      const kByte = k[i] || 0;
      innerPadding[i] = kByte ^ 0x36;
      outerPadding[i] = kByte ^ 0x5c;
    }

    // 计算内部哈希
    const innerHash = SHA256(innerPadding.concat(
      typeof message === 'string' ? stringToBytes(message) : message
    ));

    // 计算外部哈希
    return SHA256(outerPadding.concat(wordsToBytes(innerHash)));
  }

  return {
    SHA256: function(message) {
      const hash = SHA256(message);
      return {
        toString: function(encoder) {
          return (encoder || {}).stringify(hash);
        }
      };
    },
    HmacSHA256: function(message, key) {
      const hash = HMAC_SHA256(key, message);
      return {
        toString: function(encoder) {
          return (encoder || {}).stringify(hash);
        }
      };
    },
    enc: {
      Hex: {
        stringify: wordsToHex
      },
      Latin1: {
        stringify: wordsToString
      }
    }
  };
})();

module.exports = CryptoJSMini; 